#include "TextureFrame.h"

#include <filesystem/String.hpp>
#include <lib3d/hard/Hardware.h>
#include "TextureSimple.h"
#include "TextureAtlas.h"
#include <stdexcept>


namespace liba {
namespace lib3d {
namespace material {

TextureFrame::TextureFrame(const filesystem::String &simpleTextureFileName, bool wrap)
	: atlasFrame(0)
	, frameIndex(-1)
{
	TextureSimpleName name(simpleTextureFileName, wrap);
	simpleTexture.reset(new TextureSimple(Atom(), name));
}
	
TextureFrame::TextureFrame(hard::Hardware * hardware, hard::Texture * texture)
	: atlasFrame(0)
	, frameIndex(-1)
{
	simpleTexture.reset(new TextureSimple(hardware, texture));
}

TextureFrame::TextureFrame(const filesystem::String &atlasTextureFileName, const Atom &frameName)
	: atlasFrame(0)
	, frameIndex(-1)
{
	atlasTexture.reset(new TextureAtlas(atlasTextureFileName));
	this->atlasFrameName = frameName;
}

TextureFrame::TextureFrame(const filesystem::String &atlasTextureFileName, int frameIndex)
	: atlasFrame(0)
	, frameIndex(-1)
{
	atlasTexture.reset(new TextureAtlas(atlasTextureFileName));
	this->frameIndex = frameIndex;
}

TextureFrame::~TextureFrame()
{
}

TextureFrame::TextureFrame(const TextureFrame& rhs)
	: atlasFrame(rhs.atlasFrame)
	, atlasFrameName(rhs.atlasFrameName)
	, frameIndex(rhs.frameIndex)
{
	if(rhs.simpleTexture.get())
		simpleTexture.reset(new TextureSimple(Atom(), rhs.simpleTexture->get_rep_name()));
	if(rhs.atlasTexture.get())
		atlasTexture.reset(new TextureAtlas(rhs.atlasTexture->get_rep_name()));
}

inline bool TextureFrame::IsSimpleTextureLoaded()
{
	return simpleTexture.get() && simpleTexture->get_rep() && simpleTexture->get_rep()->is_loaded();
}

int TextureFrame::GetWidth()
{
	if(IsSimpleTextureLoaded())
		return simpleTexture->get_rep()->get_width(true);
	if(GetAtlasFrame())
		return GetAtlasFrame()->rect.width;

	return 0;
}

int TextureFrame::GetHeight()
{
	if(IsSimpleTextureLoaded())
		return simpleTexture->get_rep()->get_height(true);
	if(GetAtlasFrame())
		return GetAtlasFrame()->rect.height;

	return 0;
}

void TextureFrame::SetAtlasFrameName(const Atom &frameName)
{
	if(GetAtlasFrame())
		atlasFrame = const_cast <AtlasFrame*> (&atlasTexture->get_rep()->GetFrame(frameName));
}

void TextureFrame::SetAtlasFrameIndex(int frameIndex)
{
	if(GetAtlasFrame())
		atlasFrame = const_cast <AtlasFrame*> (&atlasTexture->get_rep()->GetFrame(frameIndex));
}

AtlasFrame* TextureFrame::GetAtlasFrame()
{
	if(atlasFrame)
		return atlasFrame;

	if(atlasTexture.get() && atlasTexture->get_rep() && atlasTexture->get_rep()->is_loaded())
	{
		if(this->frameIndex >= 0)
		{
			return atlasFrame = const_cast <AtlasFrame*> (&atlasTexture->get_rep()->GetFrame(frameIndex));
		}
		else
		{
			return atlasFrame = const_cast <AtlasFrame*> (&atlasTexture->get_rep()->GetFrame(atlasFrameName));
		}
	}

	return 0;
}

float TextureFrame::TransformU(float u)
{
	if(GetAtlasFrame())
	{
		int width = atlasTexture->get_rep()->GetWidth();
		// TODO: Make preload sizes!!!
		if (!width)
			throw std::overflow_error("TextureFrame::TransformU: width == 0");
		return (GetAtlasFrame()->rect.x + GetAtlasFrame()->rect.width * u) / width;
	}

	return u;
}

float TextureFrame::TransformV(float v)
{
	if(GetAtlasFrame())
	{
		int height = atlasTexture->get_rep()->GetHeight();
		// TODO: Make preload sizes!!!
		if (!height)
			throw std::overflow_error("TextureFrame::TransformU: height == 0");
		return ((height - GetAtlasFrame()->rect.y - GetAtlasFrame()->rect.height) + GetAtlasFrame()->rect.height * v) / height;
	}

	return v;
}

bool TextureFrame::IsReady()
{
	return IsSimpleTextureLoaded() || GetAtlasFrame();
}

void TextureFrame::render_to_list(hard::Hardware * hardware, hard::RenderTexture * render_texture)
{
	if(GetAtlasFrame())
		return atlasTexture->render_to_list(hardware, render_texture);
	if(IsSimpleTextureLoaded())
		return simpleTexture->render_to_list(hardware, render_texture);
}

} // namespace material
} // namespace lib3d
} // namespace liba
